import { UPLOADERROR } from "./errorType";
import {STATIC_TIMEOUT, STATIC_RESPONSE_ERROR,  STATIC_EXCHANGE_ERROR} from './statusEnum'


// TODO: 上传后续考虑 业务传入的 axios
class XHRUpload {
  constructor({
    changeType = "image/jpeg",
    base64 = null,
    beforeUpload,
    uploading,
    successUpload,
    errorUpload,
    xhrParam,
    uploadUrl,
    uploadTimeout,
    formdataName = "file",
    fileName='blob'
  }) {
    this.base64 = base64;
    this.beforeUpload = beforeUpload;
    this.uploading = uploading;
    this.successUpload = successUpload;
    this.errorUpload = errorUpload;
    this.changeType = changeType;
    this.uploadUrl = uploadUrl;
    this.uploadTimeout = uploadTimeout;
    // 上传的额外参数
    this.xhrParam = xhrParam;
    this.formdataName = formdataName;
    this.fileName = fileName;
    this.xhrUpload(base64);
  }

  xhrUpload(dataBase64) {
    const xhr = new XMLHttpRequest();
    const formdata = this.getFormData();
    const blob = this.dataURLtoBlob(dataBase64);
    const filename = this.fileName;
    formdata.append(this.formdataName, blob, filename);
    // 这里是重写XHR 所以这个xhrParam必须是对象
    const xhrParam = this.xhrParam;
    if (xhrParam && xhrParam instanceof Object) {
      Object.keys(this.xhrParam).map(keys => {
        formdata.append(keys, this.xhrParam[keys]);
      });
    }
    if (xhrParam && !xhrParam instanceof Object) {
      console.error("xhrParam必须是对象");
      return;
    }
    const parDate = new Date() * 1;
    xhr.open("post", `${this.uploadUrl}?_=${parDate}`);
    let timeout = this.uploadTimeout;
    const uploadTimer = setTimeout(function() {
      timeout = 0;
      xhr.abort(); // 请求中止
    }, timeout * 1000);
    xhr.onreadystatechange = () => {
      if (timeout <= 0) {
        this.errorUpload({
          message: "上传超时",
          errorType: UPLOADERROR,
          status: STATIC_TIMEOUT
        });
        xhr.abort(); // 请求中止
      }
      if (Number(xhr.readyState) === 4) {
        clearTimeout(uploadTimer);
        try {
          const responseData = JSON.parse(xhr.responseText);
          this.successUpload && this.successUpload(responseData);
        } catch (err) {
          this.errorUpload({
            message: "上传失败",
            errorType: UPLOADERROR,
            status: STATIC_RESPONSE_ERROR
          });
        }
      }
    };
    xhr.send(formdata);
  }

  /**
   * 把图片转成formdata 可以使用的数据...
   *这里要把\s替换掉..要不然atob的时候会出错....
   */
  dataURLtoBlob(data) {
    const tmp = data.split(",");
    tmp[1] = tmp[1].replace(/\s/g, "");
    const binary = atob(tmp[1]);
    const array = [];
    for (let i = 0; i < binary.length; i++) {
      array.push(binary.charCodeAt(i));
    }
    return this.getBlob(new Uint8Array(array), this.changeType);
  }

  /**
   * 获取blob对象的兼容性写法
   * @param buffer
   * @param format
   * @returns {*}
   */
  getBlob(data, datatype) {
    const _that = this;
    let result;
    try {
      if (datatype) {
        result = new Blob([data], { type: datatype });
      } else {
        result = new Blob(data);
      }
      // 一切正常,直接使用blob.
    } catch (e) {
      const BlobBuilder =
        window.BlobBuilder ||
        window.WebKitBlobBuilder ||
        window.MozBlobBuilder ||
        window.MSBlobBuilder;
      // 使用blobbuilder来生成文件..
      if (e.name === "TypeError" && BlobBuilder) {
        const bob = new BlobBuilder();
        bob.append(data.buffer);
        result = bob.getBlob(datatype);
      } else {
        _that.errorUpload({
          message: "blob转换错误",
          errorType: BLOBCHANGEERROR,
          status: STATIC_EXCHANGE_ERROR
        });
      }
    }

    return result;
  }

  /**
   * 获取formdata
   */
  getFormData() {
    if (window.FormData) {
      return new window.FormData();
    } else {
      return this.formDataShim();
    }
  }

  /**
   * formdata 补丁, 给不支持formdata上传blob的android机打补丁
   * @constructor
   */
  formDataShim() {
    const that = this;
    const parts = []; // Data to be sent
    const boundary =
      Array(5).join("-") + (+new Date() * (1e16 * Math.random())).toString(32);
    const oldSend = XMLHttpRequest.prototype.send;
    // 把xhr的send方法重写一下.
    XMLHttpRequest.prototype.send = function(xhrs) {
      // XMLHttpRequest调用send,传递 formDataShim返回值
      if (xhrs.isSelfFormdata === true) {
        const data = that.getBlob(that.parts);
        const fr = new FileReader();
        fr.onload = () => {
          oldSend.call(this, fr.result);
        };
        fr.onerror = function(err) {
          throw err;
        };
        fr.readAsArrayBuffer(data);
        // 设置content-type
        this.setRequestHeader(
          "Content-Type",
          `multipart/form-data; boundary=${boundary}`
        );
        XMLHttpRequest.prototype.send = oldSend;
      } else {
        oldSend.call(this, xhrs);
      }
    };
    return {
      append: (name, value, filename) => {
        parts.push(
          `\r\n--${boundary}\r\nContent-Disposition: form-data; name="${name}"`
        );

        if (value instanceof Blob) {
          parts.push(
            `; filename="blob"\r\nContent-Type: ${value.type}\r\n\r\n`
          );
          parts.push(value);
        } else {
          parts.push(`\r\n\r\n${value}`);
        }
        parts.push("\r\n");
        // 最后加一下boundary..注意这里一定要在最后加\r\n..否则服务器有可能会解析参数失败..
        parts.push(`\r\n--${boundary}--\r\n`);
        this.parts = parts;
      },
      isSelfFormdata: true
    };
  }
}

export default (...args) => {
  return new XHRUpload(...args);
};
